/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 * 
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/**
  * Create an instance of BTPeerManagerListener
  * 
  * @constructor
  * @param {BTPeerManager} peerManager 
  */
BTPeerManagerPeerListener = function(peerManager) {
    this._peerManager = peerManager;
}
BTPeerManagerPeerListener.prototype = {
    /**
      *
      * @param {BitTorrentPeer} peer
      * @param {nsIVariant} information
      */
    update : function(/* BitTorrentPeer */ peer, /* nsIVariant */ information) {
               
    },

    /**
      *
      * @param {BitTorrentPeer} peer
      * @param {nsIVariant} information
      */
    onDisconnect : function(/* nsIBitTorrentPeer */ peer, /* nsIVariant */ information) {
        // Slow...
        var peerId;
        for (peerId in this._peerManager._peers) {
            if (this._peerManager._peers[peerId] == peer) {
                delete this._peerManager._peers[peerId];
                break;
            }
        }
        peer.removePeerListener(this);
    },

    /**
      *
      * @param {BitTorrentPeer} peer
      * @param {nsIVariant} information
      */
    onError : function(/* nsIBitTorrentPeer */ tracker, /* nsIVariant */ information) {

    }
}

/**
 * @constructor
 */
KeepAliveTimeoutHandler = function (peerManager) {
    this._peerManager = peerManager;
}

/**
 *
 * @param {nsITimer} timer
 */
TrackerTimeoutHandler.prototype.notify = function(timer) {
    this._peerManager._checkKeepAlive();
}

/**
  * Create an instance of BTPeerManager
  * 
  * @constructor
  */
BTPeerManager = function() {
    var this_BTPeerManager = this;
    this._remotePeerFactory = null;
    this._peers = {};
    this._peerEventListener = new BTPeerManagerPeerListener(this);
    this._keepAliveTimeoutHandler = new KeepAliveTimeoutHandler(this);   // Object to handle timeout callbacks, avoiding closures.
    this._server = null;
    this._keepAliveInterval = 30;
    this._active = false;
    this._timer = Components.classes["@mozilla.org/timer;1"]
       .createInstance(Components.interfaces.nsITimer); // Timer for keepalives with peers
};

BTPeerManager.prototype = {
    classDescription : "BitTorrent Peer Manager",
    contractID : "@wikiscraps.com/manage/btSimplePeerManager;1",
    classID : Components.ID("95d412c2-3826-4c26-93f6-c7dd3adfdeb7"),
    QueryInterface : XPCOMUtils.generateQI(
        [Components.interfaces.BitTorrentManager
        ,Components.interfaces.nsISupports]),
    setRemotePeerFactory : function(/* RemotePeerFactory */ peerFactory) {
        this._remotePeerFactory = peerFactory;
    },
    addPeer : function(/* ACString */ ip, /* int */ port, /* ACString */ peerId) {
        var peerFullId = ((peerId != null && peerId.length > 0) ? peerId + "@" : "" )
            + ip + ":" + port;
        // Dumb incarnation: just add every darn peer if we don't appear to have it
        if (!(peerFullId in this._peers)) {        
            (this._peers[ peerFullId ] = this._remotePeerFactory.createRemotePeer(ip, port, peerId))
                .addPeerListener(this._peerEventListener);
        }
    },
    addExistingPeer : function(/* BitTorrentPeer */ peer) {
        var peerFullId = ((peer.peerIndentity != null && peer.peerIndentity.length > 0) ? peer.peerIndentity + "@" : "" )
            + ip + ":" + port;
        // Dumb version: just add every distinct peer
        if (!(peerFullId in this._peers)) {        
            (this._peers[ peerFullId ] = peer)
                .addPeerListener(this._peerEventListener);
        }
    },
    setBitTorrentServer : function(/* BitTorrentServer */ server) {
        this._server = server;
    },
    stop : function() {
        var peerId;
        if (this._active == false) {
            // error
            // Not fatal, and the resulting state will be as the caller expects (ie. stopped)
            // so don't throw exception.
            reportWarning("Tracker.stop : call to stop when tracker not running");
        } else {
            this._active = false;
            this._timer.cancel();
        }
        for (peerId in this._peers) {
            // Don't remove peer from list or remove listeners,
            // that is done after disconnect.
            this._peers[peerId].stop();
        }

    },
    start : function() {
        // Start timer
        if (this._active == false) {
            this._resetTimer();
        }
    },
    _checkKeepAlive : function() {
        var peerId;
        var peer;
        var now = Date.now();
        for (peerId in this._peers) {
            peer = this._peers[peerId];
            if (peer.lastReceived + this.MAX_PEER_TIMEOUT < now) {
                // past the max timeout, connection should be severed
                peer.stop();
            } else if (peer.lastSent + this.KEEPALIVE_TIMEOUT > now) {
                // Send keepalive
                // TODO
            }
        }
    },
    _resetTimer : function() {
        // Prepare for keepalives
        this._active = true;
        this._timer.cancel(); // clear any existing timeout
        if (this._keepAliveInterval > 0) {
            // Reset the timer. If interval not correctly set, expect we will either:
            // a) Not be making a future request
            // b) Will reset the timer later when we know the correct interval, and recall _resetTimer.
            this._timer.initWithCallback(
                this._keepAliveTimeoutHandler,
                this._keepAliveInterval  * 1000,
                Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
        }
    }
}

var NSGetModule = XPCOMUtils.generateNSGetModule([BTPeerManager]);

